summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCharles Lombardo <clombardo169@gmail.com>2023-03-21 06:58:25 +0100
committerbunnei <bunneidev@gmail.com>2023-06-03 09:05:46 +0200
commit273e81bb94f800e08686ae2f02226d93d61825de (patch)
tree8df1b38df9c920fec7542c9fe78c86de635922cd
parentandroid: Make Game class parcelable (diff)
downloadyuzu-273e81bb94f800e08686ae2f02226d93d61825de.tar
yuzu-273e81bb94f800e08686ae2f02226d93d61825de.tar.gz
yuzu-273e81bb94f800e08686ae2f02226d93d61825de.tar.bz2
yuzu-273e81bb94f800e08686ae2f02226d93d61825de.tar.lz
yuzu-273e81bb94f800e08686ae2f02226d93d61825de.tar.xz
yuzu-273e81bb94f800e08686ae2f02226d93d61825de.tar.zst
yuzu-273e81bb94f800e08686ae2f02226d93d61825de.zip
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt127
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt5
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt121
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MenuFragment.java129
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InsetsHelper.kt8
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/SerializableHelper.kt37
-rw-r--r--src/android/app/src/main/res/drawable/ic_controller.xml9
-rw-r--r--src/android/app/src/main/res/drawable/ic_exit.xml10
-rw-r--r--src/android/app/src/main/res/drawable/ic_pause.xml9
-rw-r--r--src/android/app/src/main/res/drawable/ic_play.xml9
-rw-r--r--src/android/app/src/main/res/layout/activity_emulation.xml33
-rw-r--r--src/android/app/src/main/res/layout/fragment_emulation.xml92
-rw-r--r--src/android/app/src/main/res/layout/fragment_ingame_menu.xml56
-rw-r--r--src/android/app/src/main/res/layout/header_in_game.xml23
-rw-r--r--src/android/app/src/main/res/menu/menu_in_game.xml32
-rw-r--r--src/android/app/src/main/res/menu/menu_overlay_options.xml12
-rw-r--r--src/android/app/src/main/res/values/strings.xml4
17 files changed, 343 insertions, 373 deletions
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
index b222344c3..0da7562a6 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
@@ -8,15 +8,10 @@ import android.content.DialogInterface
import android.content.Intent
import android.graphics.Rect
import android.os.Bundle
-import android.view.MotionEvent
import android.view.View
import android.view.WindowManager
-import androidx.activity.OnBackPressedCallback
-import androidx.annotation.IntDef
import androidx.appcompat.app.AppCompatActivity
-import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
-import androidx.fragment.app.FragmentManager
import androidx.preference.PreferenceManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.slider.Slider.OnChangeListener
@@ -25,8 +20,9 @@ import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.DialogSliderBinding
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.fragments.EmulationFragment
-import org.yuzu.yuzu_emu.fragments.MenuFragment
+import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.utils.ControllerMappingHelper
+import org.yuzu.yuzu_emu.utils.SerializableHelper.parcelable
import org.yuzu.yuzu_emu.utils.ThemeHelper
import kotlin.math.roundToInt
@@ -37,11 +33,11 @@ open class EmulationActivity : AppCompatActivity() {
//private Intent foregroundService;
var isActivityRecreated = false
- private var selectedTitle: String? = null
- private var path: String? = null
private var menuVisible = false
private var emulationFragment: EmulationFragment? = null
+ private lateinit var game: Game
+
override fun onDestroy() {
// TODO(bunnei): Disable notifications until we support app suspension.
//stopService(foregroundService);
@@ -54,9 +50,7 @@ open class EmulationActivity : AppCompatActivity() {
super.onCreate(savedInstanceState)
if (savedInstanceState == null) {
// Get params we were passed
- val gameToEmulate = intent
- path = gameToEmulate.getStringExtra(EXTRA_SELECTED_GAME)
- selectedTitle = gameToEmulate.getStringExtra(EXTRA_SELECTED_TITLE)
+ game = intent.parcelable(EXTRA_SELECTED_GAME)!!
isActivityRecreated = false
} else {
isActivityRecreated = true
@@ -73,34 +67,26 @@ open class EmulationActivity : AppCompatActivity() {
emulationFragment =
supportFragmentManager.findFragmentById(R.id.frame_emulation_fragment) as EmulationFragment?
if (emulationFragment == null) {
- emulationFragment = EmulationFragment.newInstance(path)
+ emulationFragment = EmulationFragment.newInstance(game)
supportFragmentManager.beginTransaction()
.add(R.id.frame_emulation_fragment, emulationFragment!!)
.commit()
}
- title = selectedTitle
+ title = game.title
// Start a foreground service to prevent the app from getting killed in the background
// TODO(bunnei): Disable notifications until we support app suspension.
//foregroundService = new Intent(EmulationActivity.this, ForegroundService.class);
//startForegroundService(foregroundService);
-
- onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
- override fun handleOnBackPressed() {
- toggleMenu()
- }
- })
}
override fun onSaveInstanceState(outState: Bundle) {
- outState.putString(EXTRA_SELECTED_GAME, path)
- outState.putString(EXTRA_SELECTED_TITLE, selectedTitle)
+ outState.putParcelable(EXTRA_SELECTED_GAME, game)
super.onSaveInstanceState(outState)
}
private fun restoreState(savedInstanceState: Bundle) {
- path = savedInstanceState.getString(EXTRA_SELECTED_GAME)
- selectedTitle = savedInstanceState.getString(EXTRA_SELECTED_TITLE)
+ game = savedInstanceState.parcelable(EXTRA_SELECTED_GAME)!!
// If an alert prompt was in progress when state was restored, retry displaying it
NativeLibrary.retryDisplayAlertPrompt()
@@ -110,6 +96,8 @@ open class EmulationActivity : AppCompatActivity() {
window.attributes.layoutInDisplayCutoutMode =
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
+ window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN or WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS)
+
// It would be nice to use IMMERSIVE_STICKY, but that doesn't show the toolbar.
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
@@ -119,15 +107,6 @@ open class EmulationActivity : AppCompatActivity() {
View.SYSTEM_UI_FLAG_IMMERSIVE
}
- fun handleMenuAction(action: Int) {
- when (action) {
- MENU_ACTION_EXIT -> {
- emulationFragment!!.stopEmulation()
- finish()
- }
- }
- }
-
private fun editControlsPlacement() {
if (emulationFragment!!.isConfiguringControls) {
emulationFragment!!.stopConfiguringControls()
@@ -176,94 +155,14 @@ open class EmulationActivity : AppCompatActivity() {
.show()
}
- override fun dispatchTouchEvent(event: MotionEvent): Boolean {
- if (event.actionMasked == MotionEvent.ACTION_DOWN) {
- var anyMenuClosed = false
- var submenu = supportFragmentManager.findFragmentById(R.id.frame_submenu)
- if (submenu != null && areCoordinatesOutside(submenu.view, event.x, event.y)) {
- closeSubmenu()
- submenu = null
- anyMenuClosed = true
- }
- if (submenu == null) {
- val menu = supportFragmentManager.findFragmentById(R.id.frame_menu)
- if (menu != null && areCoordinatesOutside(menu.view, event.x, event.y)) {
- closeMenu()
- anyMenuClosed = true
- }
- }
- if (anyMenuClosed) {
- return true
- }
- }
- return super.dispatchTouchEvent(event)
- }
-
- @Retention(AnnotationRetention.SOURCE)
- @IntDef(
- MENU_ACTION_EDIT_CONTROLS_PLACEMENT,
- MENU_ACTION_TOGGLE_CONTROLS,
- MENU_ACTION_ADJUST_SCALE,
- MENU_ACTION_EXIT,
- MENU_ACTION_SHOW_FPS,
- MENU_ACTION_RESET_OVERLAY,
- MENU_ACTION_SHOW_OVERLAY,
- MENU_ACTION_OPEN_SETTINGS
- )
- annotation class MenuAction
-
- private fun closeSubmenu(): Boolean {
- return supportFragmentManager.popBackStackImmediate(
- BACKSTACK_NAME_SUBMENU,
- FragmentManager.POP_BACK_STACK_INCLUSIVE
- )
- }
-
- private fun closeMenu(): Boolean {
- menuVisible = false
- return supportFragmentManager.popBackStackImmediate(
- BACKSTACK_NAME_MENU,
- FragmentManager.POP_BACK_STACK_INCLUSIVE
- )
- }
-
- private fun toggleMenu() {
- if (!closeMenu()) {
- val fragment: Fragment = MenuFragment.newInstance()
- supportFragmentManager.beginTransaction()
- .setCustomAnimations(
- R.animator.menu_slide_in_from_start,
- R.animator.menu_slide_out_to_start,
- R.animator.menu_slide_in_from_start,
- R.animator.menu_slide_out_to_start
- )
- .add(R.id.frame_menu, fragment)
- .addToBackStack(BACKSTACK_NAME_MENU)
- .commit()
- menuVisible = true
- }
- }
-
companion object {
- private const val BACKSTACK_NAME_MENU = "menu"
- private const val BACKSTACK_NAME_SUBMENU = "submenu"
const val EXTRA_SELECTED_GAME = "SelectedGame"
- const val EXTRA_SELECTED_TITLE = "SelectedTitle"
- const val MENU_ACTION_EDIT_CONTROLS_PLACEMENT = 0
- const val MENU_ACTION_TOGGLE_CONTROLS = 1
- const val MENU_ACTION_ADJUST_SCALE = 2
- const val MENU_ACTION_EXIT = 3
- const val MENU_ACTION_SHOW_FPS = 4
- const val MENU_ACTION_RESET_OVERLAY = 6
- const val MENU_ACTION_SHOW_OVERLAY = 7
- const val MENU_ACTION_OPEN_SETTINGS = 8
private const val EMULATION_RUNNING_NOTIFICATION = 0x1000
@JvmStatic
- fun launch(activity: FragmentActivity, path: String?, title: String?) {
+ fun launch(activity: FragmentActivity, game: Game) {
val launcher = Intent(activity, EmulationActivity::class.java)
- launcher.putExtra(EXTRA_SELECTED_GAME, path)
- launcher.putExtra(EXTRA_SELECTED_TITLE, title)
+ launcher.putExtra(EXTRA_SELECTED_GAME, game)
activity.startActivity(launcher)
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
index e9f926d84..0295801ad 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
@@ -13,7 +13,6 @@ import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import androidx.appcompat.app.AppCompatActivity
-import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.RecyclerView
import coil.load
@@ -23,8 +22,8 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
-import org.yuzu.yuzu_emu.activities.EmulationActivity.Companion.launch
import org.yuzu.yuzu_emu.databinding.CardGameBinding
+import org.yuzu.yuzu_emu.activities.EmulationActivity
import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.model.GameDatabase
import org.yuzu.yuzu_emu.utils.Log
@@ -181,7 +180,7 @@ class GameAdapter(private val activity: AppCompatActivity) : RecyclerView.Adapte
*/
override fun onClick(view: View) {
val holder = view.tag as GameViewHolder
- launch((view.context as FragmentActivity), holder.game.path, holder.game.title)
+ EmulationActivity.launch((view.context as AppCompatActivity), holder.game)
}
private fun isValidGame(path: String): Boolean {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
index 0889b6f7f..4ba283ddd 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
@@ -10,7 +10,14 @@ import android.graphics.Color
import android.os.Bundle
import android.os.Handler
import android.view.*
+import android.widget.TextView
import android.widget.Toast
+import androidx.activity.OnBackPressedCallback
+import androidx.appcompat.widget.PopupMenu
+import androidx.core.content.res.ResourcesCompat
+import androidx.core.graphics.Insets
+import androidx.core.view.ViewCompat
+import androidx.core.view.WindowInsetsCompat
import androidx.fragment.app.Fragment
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import androidx.preference.PreferenceManager
@@ -20,10 +27,15 @@ import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.activities.EmulationActivity
import org.yuzu.yuzu_emu.databinding.FragmentEmulationBinding
import org.yuzu.yuzu_emu.features.settings.model.Settings
+import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity
+import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
+import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.DirectoryInitialization.DirectoryInitializationState
import org.yuzu.yuzu_emu.utils.DirectoryStateReceiver
+import org.yuzu.yuzu_emu.utils.InsetsHelper
import org.yuzu.yuzu_emu.utils.Log
+import org.yuzu.yuzu_emu.utils.SerializableHelper.parcelable
class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.FrameCallback {
private lateinit var preferences: SharedPreferences
@@ -35,6 +47,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
private var _binding: FragmentEmulationBinding? = null
private val binding get() = _binding!!
+ private lateinit var game: Game
+
override fun onAttach(context: Context) {
super.onAttach(context)
if (context is EmulationActivity) {
@@ -54,8 +68,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
// So this fragment doesn't restart on configuration changes; i.e. rotation.
retainInstance = true
preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
- val gamePath = requireArguments().getString(KEY_GAMEPATH)
- emulationState = EmulationState(gamePath)
+ game = requireArguments().parcelable(EmulationActivity.EXTRA_SELECTED_GAME)!!
+ emulationState = EmulationState(game.path)
}
/**
@@ -78,6 +92,57 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
// Setup overlay.
resetInputOverlay()
updateShowFpsOverlay()
+
+ binding.inGameMenu.getHeaderView(0).findViewById<TextView>(R.id.text_game_title).text =
+ game.title
+ binding.inGameMenu.setNavigationItemSelectedListener {
+ when (it.itemId) {
+ R.id.menu_pause_emulation -> {
+ if (emulationState.isPaused) {
+ emulationState.run(false)
+ it.title = resources.getString(R.string.emulation_pause)
+ it.icon = ResourcesCompat.getDrawable(
+ resources,
+ R.drawable.ic_pause,
+ requireContext().theme
+ )
+ } else {
+ emulationState.pause()
+ it.title = resources.getString(R.string.emulation_unpause)
+ it.icon = ResourcesCompat.getDrawable(
+ resources,
+ R.drawable.ic_play,
+ requireContext().theme
+ )
+ }
+ true
+ }
+ R.id.menu_settings -> {
+ SettingsActivity.launch(requireContext(), SettingsFile.FILE_NAME_CONFIG, "")
+ true
+ }
+ R.id.menu_overlay_controls -> {
+ showOverlayOptions()
+ true
+ }
+ R.id.menu_exit -> {
+ requireActivity().finish()
+ emulationState.stop()
+ true
+ }
+ else -> true
+ }
+ }
+
+ setInsets()
+
+ requireActivity().onBackPressedDispatcher.addCallback(
+ requireActivity(),
+ object : OnBackPressedCallback(true) {
+ override fun handleOnBackPressed() {
+ if (binding.drawerLayout.isOpen) binding.drawerLayout.close() else binding.drawerLayout.open()
+ }
+ })
}
override fun onResume() {
@@ -202,8 +267,30 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
NativeLibrary.DoFrame()
}
- fun stopEmulation() {
- emulationState.stop()
+ private fun showOverlayOptions() {
+ val anchor = binding.inGameMenu.findViewById<View>(R.id.menu_overlay_controls)
+ val popup = PopupMenu(requireContext(), anchor)
+
+ popup.menuInflater.inflate(R.menu.menu_overlay_options, popup.menu)
+
+ popup.setOnMenuItemClickListener {
+ when (it.itemId) {
+ R.id.menu_edit_overlay -> {
+ binding.drawerLayout.close()
+ binding.surfaceInputOverlay.requestFocus()
+ startConfiguringControls()
+ true
+ }
+ R.id.menu_reset_overlay -> {
+ binding.drawerLayout.close()
+ resetInputOverlay()
+ true
+ }
+ else -> true
+ }
+ }
+
+ popup.show()
}
fun startConfiguringControls() {
@@ -219,6 +306,27 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
val isConfiguringControls: Boolean
get() = binding.surfaceInputOverlay.isInEditMode
+ private fun setInsets() {
+ ViewCompat.setOnApplyWindowInsetsListener(binding.inGameMenu) { v: View, windowInsets: WindowInsetsCompat ->
+ val cutInsets: Insets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
+ var left = 0
+ var right = 0
+ if (ViewCompat.getLayoutDirection(v) == ViewCompat.LAYOUT_DIRECTION_LTR) {
+ left = cutInsets.left
+ } else {
+ right = cutInsets.right
+ }
+
+ // Don't use padding if the navigation bar isn't in the way
+ if (InsetsHelper.getBottomPaddingRequired(requireActivity()) > 0) {
+ v.setPadding(left, cutInsets.top, right, 0)
+ } else {
+ v.setPadding(left, cutInsets.top, right, 0)
+ }
+ windowInsets
+ }
+ }
+
private class EmulationState(private val mGamePath: String?) {
private var state: State
private var surface: Surface? = null
@@ -340,12 +448,11 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
}
companion object {
- private const val KEY_GAMEPATH = "gamepath"
private val perfStatsUpdateHandler = Handler()
- fun newInstance(gamePath: String?): EmulationFragment {
+ fun newInstance(game: Game): EmulationFragment {
val args = Bundle()
- args.putString(KEY_GAMEPATH, gamePath)
+ args.putParcelable(EmulationActivity.EXTRA_SELECTED_GAME, game)
val fragment = EmulationFragment()
fragment.arguments = args
return fragment
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MenuFragment.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MenuFragment.java
deleted file mode 100644
index 5dc3f5545..000000000
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MenuFragment.java
+++ /dev/null
@@ -1,129 +0,0 @@
-package org.yuzu.yuzu_emu.fragments;
-
-import android.content.pm.PackageManager;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.util.SparseIntArray;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Button;
-import android.widget.LinearLayout;
-
-import androidx.annotation.ColorInt;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.core.graphics.Insets;
-import androidx.core.view.ViewCompat;
-import androidx.core.view.WindowInsetsCompat;
-import androidx.fragment.app.Fragment;
-
-import com.google.android.material.color.MaterialColors;
-import com.google.android.material.elevation.ElevationOverlayProvider;
-
-import org.yuzu.yuzu_emu.R;
-import org.yuzu.yuzu_emu.activities.EmulationActivity;
-
-
-public final class MenuFragment extends Fragment implements View.OnClickListener
-{
- private static final String KEY_TITLE = "title";
- private static final String KEY_WII = "wii";
- private static SparseIntArray buttonsActionsMap = new SparseIntArray();
-
- private int mCutInset = 0;
-
- static
- {
- buttonsActionsMap.append(R.id.menu_exit, EmulationActivity.MENU_ACTION_EXIT);
- }
-
- public static MenuFragment newInstance()
- {
- MenuFragment fragment = new MenuFragment();
-
- Bundle arguments = new Bundle();
- fragment.setArguments(arguments);
-
- return fragment;
- }
-
- // This is primarily intended to account for any navigation bar at the bottom of the screen
- private int getBottomPaddingRequired()
- {
- Rect visibleFrame = new Rect();
- requireActivity().getWindow().getDecorView().getWindowVisibleDisplayFrame(visibleFrame);
- return visibleFrame.bottom - visibleFrame.top - getResources().getDisplayMetrics().heightPixels;
- }
-
- @NonNull
- @Override
- public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState)
- {
- View rootView = inflater.inflate(R.layout.fragment_ingame_menu, container, false);
-
- LinearLayout options = rootView.findViewById(R.id.layout_options);
-
-// mPauseEmulation = options.findViewById(R.id.menu_pause_emulation);
-// mUnpauseEmulation = options.findViewById(R.id.menu_unpause_emulation);
-//
-// updatePauseUnpauseVisibility();
-//
-// if (!requireActivity().getPackageManager().hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN))
-// {
-// options.findViewById(R.id.menu_overlay_controls).setVisibility(View.GONE);
-// }
-//
-// if (!getArguments().getBoolean(KEY_WII, true))
-// {
-// options.findViewById(R.id.menu_refresh_wiimotes).setVisibility(View.GONE);
-// }
-
- int bottomPaddingRequired = getBottomPaddingRequired();
-
- // Provide a safe zone between the navigation bar and Exit Emulation to avoid accidental touches
- float density = getResources().getDisplayMetrics().density;
- if (bottomPaddingRequired >= 32 * density)
- {
- bottomPaddingRequired += 32 * density;
- }
-
- if (bottomPaddingRequired > rootView.getPaddingBottom())
- {
- rootView.setPadding(rootView.getPaddingLeft(), rootView.getPaddingTop(),
- rootView.getPaddingRight(), bottomPaddingRequired);
- }
-
- for (int childIndex = 0; childIndex < options.getChildCount(); childIndex++)
- {
- Button button = (Button) options.getChildAt(childIndex);
-
- button.setOnClickListener(this);
- }
-
- rootView.findViewById(R.id.menu_exit).setOnClickListener(this);
-
-// mTitleText = rootView.findViewById(R.id.text_game_title);
-// String title = getArguments().getString(KEY_TITLE, null);
-// if (title != null)
-// {
-// mTitleText.setText(title);
-// }
-
- if (getResources().getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_LTR)
- {
-// rootView.post(() -> NativeLibrary.SetObscuredPixelsLeft(rootView.getWidth()));
- }
-
- return rootView;
- }
-
- @Override
- public void onClick(View button)
- {
- int action = buttonsActionsMap.get(button.getId());
- EmulationActivity activity = (EmulationActivity) requireActivity();
- activity.handleMenuAction(action);
- }
-}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InsetsHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InsetsHelper.kt
index 37f08ac26..e7a04d917 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InsetsHelper.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InsetsHelper.kt
@@ -1,7 +1,9 @@
package org.yuzu.yuzu_emu.utils
import android.annotation.SuppressLint
+import android.app.Activity
import android.content.Context
+import android.graphics.Rect
import android.view.ViewGroup.MarginLayoutParams
import androidx.core.graphics.Insets
import com.google.android.material.appbar.AppBarLayout
@@ -27,4 +29,10 @@ object InsetsHelper {
resources.getInteger(resourceId)
} else 0
}
+
+ fun getBottomPaddingRequired(activity: Activity): Int {
+ val visibleFrame = Rect()
+ activity.window.decorView.getWindowVisibleDisplayFrame(visibleFrame)
+ return visibleFrame.bottom - visibleFrame.top - activity.resources.displayMetrics.heightPixels
+ }
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/SerializableHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/SerializableHelper.kt
new file mode 100644
index 000000000..23ffbaf68
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/SerializableHelper.kt
@@ -0,0 +1,37 @@
+package org.yuzu.yuzu_emu.utils
+
+import android.content.Intent
+import android.os.Build
+import android.os.Bundle
+import android.os.Parcelable
+import java.io.Serializable
+
+object SerializableHelper {
+ inline fun <reified T : Serializable> Bundle.serializable(key: String): T? {
+ return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
+ getSerializable(key, T::class.java)
+ else
+ getSerializable(key) as? T
+ }
+
+ inline fun <reified T : Serializable> Intent.serializable(key: String): T? {
+ return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
+ getSerializableExtra(key, T::class.java)
+ else
+ getSerializableExtra(key) as? T
+ }
+
+ inline fun <reified T : Parcelable> Bundle.parcelable(key: String): T? {
+ return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
+ getParcelable(key, T::class.java)
+ else
+ getParcelable(key) as? T
+ }
+
+ inline fun <reified T : Parcelable> Intent.parcelable(key: String): T? {
+ return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
+ getParcelableExtra(key, T::class.java)
+ else
+ getParcelableExtra(key) as? T
+ }
+}
diff --git a/src/android/app/src/main/res/drawable/ic_controller.xml b/src/android/app/src/main/res/drawable/ic_controller.xml
new file mode 100644
index 000000000..2359c35be
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/ic_controller.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <path
+ android:fillColor="?attr/colorControlNormal"
+ android:pathData="M21,6L3,6c-1.1,0 -2,0.9 -2,2v8c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2L23,8c0,-1.1 -0.9,-2 -2,-2zM11,13L8,13v3L6,16v-3L3,13v-2h3L6,8h2v3h3v2zM15.5,15c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5zM19.5,12c-0.83,0 -1.5,-0.67 -1.5,-1.5S18.67,9 19.5,9s1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5z" />
+</vector>
diff --git a/src/android/app/src/main/res/drawable/ic_exit.xml b/src/android/app/src/main/res/drawable/ic_exit.xml
new file mode 100644
index 000000000..a55a1d387
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/ic_exit.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:autoMirrored="true"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <path
+ android:fillColor="?attr/colorControlNormal"
+ android:pathData="M10.09,15.59L11.5,17l5,-5 -5,-5 -1.41,1.41L12.67,11H3v2h9.67l-2.58,2.59zM19,3H5c-1.11,0 -2,0.9 -2,2v4h2V5h14v14H5v-4H3v4c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2z" />
+</vector>
diff --git a/src/android/app/src/main/res/drawable/ic_pause.xml b/src/android/app/src/main/res/drawable/ic_pause.xml
new file mode 100644
index 000000000..adb3ababc
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/ic_pause.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <path
+ android:fillColor="?attr/colorControlNormal"
+ android:pathData="M6,19h4L10,5L6,5v14zM14,5v14h4L18,5h-4z" />
+</vector>
diff --git a/src/android/app/src/main/res/drawable/ic_play.xml b/src/android/app/src/main/res/drawable/ic_play.xml
new file mode 100644
index 000000000..7f01dc599
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/ic_play.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <path
+ android:fillColor="?attr/colorControlNormal"
+ android:pathData="M8,5v14l11,-7z" />
+</vector>
diff --git a/src/android/app/src/main/res/layout/activity_emulation.xml b/src/android/app/src/main/res/layout/activity_emulation.xml
index debc26e6c..f6360a65b 100644
--- a/src/android/app/src/main/res/layout/activity_emulation.xml
+++ b/src/android/app/src/main/res/layout/activity_emulation.xml
@@ -1,32 +1,13 @@
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:keepScreenOn="true"
- xmlns:tools="http://schemas.android.com/tools"
- android:id="@+id/frame_content">
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/frame_content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:keepScreenOn="true">
<FrameLayout
android:id="@+id/frame_emulation_fragment"
android:layout_width="match_parent"
- android:layout_height="match_parent"/>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="horizontal"
- android:baselineAligned="false">
-
- <FrameLayout
- android:id="@+id/frame_menu"
- android:layout_width="@dimen/menu_width"
- android:layout_height="match_parent"
- tools:layout="@layout/fragment_ingame_menu"/>
-
- <FrameLayout
- android:id="@+id/frame_submenu"
- android:layout_width="match_parent"
- android:layout_height="match_parent"/>
-
- </LinearLayout>
+ android:layout_height="match_parent" />
</FrameLayout>
diff --git a/src/android/app/src/main/res/layout/fragment_emulation.xml b/src/android/app/src/main/res/layout/fragment_emulation.xml
index a3e5707ef..be11f028f 100644
--- a/src/android/app/src/main/res/layout/fragment_emulation.xml
+++ b/src/android/app/src/main/res/layout/fragment_emulation.xml
@@ -1,47 +1,63 @@
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:keepScreenOn="true"
- tools:context="org.yuzu.yuzu_emu.fragments.EmulationFragment">
+ tools:context="org.yuzu.yuzu_emu.fragments.EmulationFragment"
+ tools:openDrawer="start">
- <!-- This is what everything is rendered to during emulation -->
- <Button
- android:id="@+id/done_control_config"
- style="@style/Widget.Material3.Button.Icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:padding="@dimen/spacing_small"
- android:text="@string/emulation_done"
- android:visibility="gone" />
-
- <!-- This is the onscreen input overlay -->
- <SurfaceView
- android:id="@+id/surface_emulation"
+ <androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:focusable="false"
- android:focusableInTouchMode="false" />
+ android:layout_height="match_parent">
- <org.yuzu.yuzu_emu.overlay.InputOverlay
- android:id="@+id/surface_input_overlay"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:focusable="true"
- android:focusableInTouchMode="true" />
+ <!-- This is the onscreen input overlay -->
+ <SurfaceView
+ android:id="@+id/surface_emulation"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:focusable="false"
+ android:focusableInTouchMode="false" />
+
+ <TextView
+ android:id="@+id/show_fps_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="18dp"
+ android:layout_marginTop="2dp"
+ android:clickable="false"
+ android:linksClickable="false"
+ android:longClickable="false"
+ android:shadowColor="@android:color/black"
+ android:textColor="@android:color/white"
+ android:textSize="12sp" />
- <TextView
- android:id="@+id/show_fps_text"
+ <org.yuzu.yuzu_emu.overlay.InputOverlay
+ android:id="@+id/surface_input_overlay"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:focusable="true"
+ android:focusableInTouchMode="true" />
+
+ <!-- This is what everything is rendered to during emulation -->
+ <Button
+ style="@style/Widget.Material3.Button.ElevatedButton"
+ android:id="@+id/done_control_config"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:text="@string/emulation_done"
+ android:visibility="gone" />
+
+ </androidx.coordinatorlayout.widget.CoordinatorLayout>
+
+ <com.google.android.material.navigation.NavigationView
+ android:id="@+id/in_game_menu"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="18dp"
- android:layout_marginTop="2dp"
- android:clickable="false"
- android:linksClickable="false"
- android:longClickable="false"
- android:shadowColor="@android:color/black"
- android:textColor="@android:color/white"
- android:textSize="12sp" />
-
-</FrameLayout>
+ android:layout_height="match_parent"
+ android:layout_gravity="start"
+ app:headerLayout="@layout/header_in_game"
+ app:menu="@menu/menu_in_game" />
+
+</androidx.drawerlayout.widget.DrawerLayout>
diff --git a/src/android/app/src/main/res/layout/fragment_ingame_menu.xml b/src/android/app/src/main/res/layout/fragment_ingame_menu.xml
deleted file mode 100644
index ce618ef7b..000000000
--- a/src/android/app/src/main/res/layout/fragment_ingame_menu.xml
+++ /dev/null
@@ -1,56 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="?attr/colorSurface"
- android:elevation="3dp"
- tools:layout_width="250dp">
-
- <TextView
- android:id="@+id/text_game_title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginHorizontal="32dp"
- android:layout_marginVertical="24dp"
- android:ellipsize="end"
- android:letterSpacing="0"
- android:maxLines="@integer/game_title_lines"
- android:textSize="20sp"
- android:textColor="?attr/colorOnSurface"
- tools:text="The Legend of Zelda: Breath of the Wild" />
-
- <com.google.android.material.divider.MaterialDivider
- android:id="@+id/divider"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
-
- <ScrollView
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:scrollbarSize="4dp"
- android:fadeScrollbars="false">
-
- <LinearLayout
- android:id="@+id/layout_options"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical" />
-
- </ScrollView>
-
- <com.google.android.material.divider.MaterialDivider
- android:id="@+id/divider_2"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
-
- <Button
- android:id="@+id/menu_exit"
- style="@style/InGameMenuOption"
- android:layout_marginTop="@dimen/spacing_large"
- android:text="@string/emulation_exit" />
-
-</LinearLayout> \ No newline at end of file
diff --git a/src/android/app/src/main/res/layout/header_in_game.xml b/src/android/app/src/main/res/layout/header_in_game.xml
new file mode 100644
index 000000000..135d429c5
--- /dev/null
+++ b/src/android/app/src/main/res/layout/header_in_game.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fitsSystemWindows="true">
+
+ <com.google.android.material.textview.MaterialTextView
+ android:id="@+id/text_game_title"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_margin="24dp"
+ android:textAppearance="?attr/textAppearanceHeadlineMedium"
+ android:textColor="?attr/colorOnSurface"
+ android:textAlignment="viewStart"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ tools:text="Super Mario Odyssey" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/src/android/app/src/main/res/menu/menu_in_game.xml b/src/android/app/src/main/res/menu/menu_in_game.xml
new file mode 100644
index 000000000..f68459640
--- /dev/null
+++ b/src/android/app/src/main/res/menu/menu_in_game.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:title="">
+
+ <menu>
+
+ <item
+ android:id="@+id/menu_pause_emulation"
+ android:icon="@drawable/ic_pause"
+ android:title="@string/emulation_pause" />
+
+ <item
+ android:id="@+id/menu_settings"
+ android:icon="@drawable/ic_settings"
+ android:title="@string/preferences_settings" />
+
+ <item
+ android:id="@+id/menu_overlay_controls"
+ android:icon="@drawable/ic_controller"
+ android:title="@string/emulation_input_overlay" />
+
+ </menu>
+
+ </item>
+
+ <item
+ android:id="@+id/menu_exit"
+ android:icon="@drawable/ic_exit"
+ android:title="@string/emulation_exit" />
+
+</menu>
diff --git a/src/android/app/src/main/res/menu/menu_overlay_options.xml b/src/android/app/src/main/res/menu/menu_overlay_options.xml
new file mode 100644
index 000000000..75c84cdf3
--- /dev/null
+++ b/src/android/app/src/main/res/menu/menu_overlay_options.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item
+ android:id="@+id/menu_edit_overlay"
+ android:title="@string/emulation_touch_overlay_edit" />
+
+ <item
+ android:id="@+id/menu_reset_overlay"
+ android:title="@string/emulation_touch_overlay_reset" />
+
+</menu>
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index 7da113728..c471425f2 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -87,6 +87,10 @@
<string name="emulation_toggle_controls">Toggle Controls</string>
<string name="emulation_control_scale">Adjust Scale</string>
<string name="emulation_touch_overlay_reset">Reset Overlay</string>
+ <string name="emulation_touch_overlay_edit">Edit Overlay</string>
+ <string name="emulation_pause">Pause Emulation</string>
+ <string name="emulation_unpause">Unpause Emulation</string>
+ <string name="emulation_input_overlay">Input Overlay</string>
<string name="load_settings">Loading Settings…</string>